<?xml version="1.0" encoding="utf-8"?>
<!--
    SVN Graph Branches
    Copyright © 2010 Olivier Mengué

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-->
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:data="uri:data">
  <xsl:strip-space elements="log logentry paths"/>
  <xsl:output method="xml" indent="yes"/>

  <xsl:param name="url"/>


  <xsl:variable name="EMBED" select="document('')//data:data"/>
  <data:data xmlns="">
    <roots>
      <root path=""/>
      <root path="/branches"/>
      <root path="/tags"/>
    </roots>
  </data:data>




  <xsl:param name="trunk-path" select="string(/log/logentry/paths/path[@action='A' and @kind!='file' and not(contains(., '/branches/') or contains(., '/tags/')) and substring(., string-length(.)-5) = '/trunk'])"/>
  <xsl:param name="module-path" select="substring($trunk-path, 1, string-length($trunk-path)-6)"/>
  <xsl:param name="module-path-slash" select="substring($trunk-path, 1, string-length($trunk-path)-5)"/>
  <!-- index to use to extract with substring() the path after $module-path -->
  <xsl:param name="after-module-path" select="string-length($module-path) + 1"/>


  <xsl:key name="revs" match="/log/logentry" use="@revision" />
  <xsl:key name="copies" match="/log/logentry[paths/path/@copyfrom-rev]" use="paths/path/@copyfrom-rev" />

  <!--
  <xsl:key name="tree-actions" match="/log/logentry/paths/path[@kind != 'file']" use="."/>
  -->
  <xsl:key name="tree-deletions" match="/log/logentry/paths/path[@action='D'][@kind != 'file']" use="."/>

  <xsl:template match="log">
    <xsl:comment>
    The SVN Forest format (.svnfr) is a private intermediary format created
    for SVN Graph Branches. The format is not fixed and is garanteed to evolve
    without prior notice.
</xsl:comment>
    <forest path="{$module-path}">
      <xsl:if test="$url">
        <xsl:attribute name="url"><xsl:value-of select="$url"/></xsl:attribute>
      </xsl:if>

      <xsl:variable name="log" select="/log"/>


      <xsl:for-each select="$EMBED/roots/root">
        <xsl:variable name="root" select="concat($module-path, @path, '/')"/>
        <xsl:variable name="after-root" select="string-length($root)+1"/>
        <xsl:apply-templates select="$log/logentry/paths/path[@action='A' and (@kind!='file') and starts-with(., $root) and not(substring(., $after-module-path) = $EMBED/roots/root/@path) and not(contains(substring(., $after-root), '/')) and (not(@copyfrom-path) or starts-with(@copyfrom-path, $module-path-slash))]" mode="root">
          <xsl:sort select="../../@revision" data-type="number" order="ascending"/>
          <!--<xsl:sort select="." order="ascending"/>-->
        </xsl:apply-templates>
      </xsl:for-each>
    </forest>

  </xsl:template>


  <xsl:template match="path" mode="root">
    <xsl:variable name="path" select="string(.)"/>
    <xsl:variable name="path-rel" select="substring($path, $after-module-path + 1)"/>

    <xsl:message><xsl:value-of select="$path-rel"/></xsl:message>

          <xsl:variable name="deleted-rev">
            <xsl:call-template name="deleted-rev"/>
          </xsl:variable>

          <xsl:variable name="last-changed-rev">
            <!-- Find the latest revision on which a file of the branch has changed after the tag -->
            <xsl:choose>
              <!-- tag/branche deleted? it's the deletion revision -->
              <xsl:when test="$deleted-rev">
                <xsl:call-template name="last-changed-rev">
                  <xsl:with-param name="path" select="."/>
                  <xsl:with-param name="before" select="number($deleted-rev)-1"/>
                </xsl:call-template>
              </xsl:when>
              <xsl:otherwise>
                <xsl:call-template name="last-changed-rev">
                  <xsl:with-param name="path" select="$path"/>
                </xsl:call-template>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:variable>

    <tree path="{$path-rel}">
          <xsl:attribute name="revision"><xsl:value-of select="../../@revision"/></xsl:attribute>
          <xsl:if test="@copyfrom-path">
            <xsl:attribute name="from-path"><xsl:value-of select="substring-after(@copyfrom-path, $module-path-slash)"/></xsl:attribute>
            <xsl:attribute name="last-changed-rev-before">
              <!-- Find the latest revision on which a file of the starting branch has changed -->
              <xsl:call-template name="last-changed-rev">
                <xsl:with-param name="path" select="@copyfrom-path"/>
                <xsl:with-param name="before" select="number(@copyfrom-rev)"/>
              </xsl:call-template>
            </xsl:attribute>
          </xsl:if>
          <xsl:attribute name="last-changed-rev"><xsl:value-of select="$last-changed-rev"/></xsl:attribute>
          <xsl:if test="$deleted-rev != ''">
            <xsl:attribute name="deleted-rev"><xsl:value-of select="$deleted-rev"/></xsl:attribute>
          </xsl:if>

          <xsl:copy-of select="../../date | ../../msg | ../../author"/>
    </tree>
  </xsl:template>


  <xsl:template name="last-changed-rev">
    <xsl:param name="path"/>
    <xsl:param name="before"/><!-- must be a number (or it will be slow) -->

    <xsl:variable name="path-slash" select="concat($path, '/')"/>

    <!--
      This code assumes that logentry nodes are ordered by revision descending.
      So the first element of the nodeset is the last revision.
    -->
    <xsl:choose>
      <xsl:when test="$before">
        <xsl:value-of select="/log/logentry[number(@revision) &lt;= $before and (paths/path = $path or starts-with(paths/path, $path-slash))][1]/@revision"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="/log/logentry[(paths/path = $path or starts-with(paths/path, $path-slash))][1]/@revision"/>
      </xsl:otherwise>
    </xsl:choose>

  </xsl:template>

  <xsl:template name="deleted-rev">
    <!-- '.' is a path -->
    <xsl:variable name="path" select="string(.)"/>
    <xsl:variable name="rev" select="number(../../@revision)"/>

    <!--
      The deletion revision of the tree is the first (in time ordering)
      deletion of a tree with the same path ($path) after the original
      revision ($rev).

      logentry are ordered by revision descending in the source document, so
      the oldest is the last one.

      Update: it looks like xsltproc doesn't respect document order in key result, so workaround using for-each
    -->
    <!--
    <xsl:value-of select="key('tree-actions', $path)[@action='D']/../../@revision[number(.) &gt; $rev][last()]"/>
    -->
    <!--
    <xsl:for-each select="key('tree-actions', $path)[@action='D']/../../@revision[number(.) &gt; $rev]">
    -->
    <xsl:for-each select="key('tree-deletions', $path)/../../@revision[number(.) &gt; $rev]">
      <xsl:sort select="." data-type="number" order="ascending"/>
      <xsl:if test="position() = 1">
        <xsl:value-of select="."/>
      </xsl:if>
    </xsl:for-each>

  </xsl:template>

</xsl:stylesheet><!-- vim:set et ts=4 sw=2 sts=2: -->
